package com.gitee.liuzhi.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author liuzhi
 * @Date 2023/8/29 22:10
 * @Version 1.0
 * @Description 反射工具
 */
@Slf4j
public class ReflectUtil {

    private static final Map<String, Field> FIELD_MAP = new HashMap<>(16);
    private static final Map<String, Method> METHOD_MAP = new HashMap<>(16);

    /**
     * get value by field
     * @param target 目标对象
     * @param fieldName 字段名
     */
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Object target, String fieldName) {
        if (target == null) {
            return null;
        }
        String key = target.getClass() + "_" + fieldName;
        Field field = FIELD_MAP.get(key);
        if (field == null) {
            field = ReflectionUtils.findField(target.getClass(), fieldName);
            if (field == null) {
                return null;
            }
            field.setAccessible(true);
            FIELD_MAP.put(key, field);
        }
        try {
            return (T)field.get(target);
        } catch (IllegalAccessException e) {
            log.error(e.getMessage());
        }
        return null;
    }

    @SuppressWarnings("ConstantConditions")
    public static Method findMethod(Object target, String methodName, Class<?> paramType) {
        String key = target.getClass() + "_" + methodName + "_" + paramType;
        Method method = METHOD_MAP.get(key);
        if (method == null) {
            do {
                method = ReflectionUtils.findMethod(target.getClass(), methodName, paramType);
                paramType = paramType.getSuperclass();
            } while (method == null || paramType == Object.class);
            if (method == null) {
                throw new RuntimeException("Not found method '" + methodName + "'");
            }
            method.setAccessible(true);
            METHOD_MAP.put(key, method);
        }
        return method;
    }

    /**
     * get value by method
     *
     * @param target 目标方法
     * @param methodName 方法名
     * @param data 方法参数
     */
    @SuppressWarnings("unchecked")
    public static <T> T getValue(Object target, String methodName, Object data) {
        Method method = findMethod(target, methodName, data.getClass());
        try {
            return (T)method.invoke(target, data);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * set value by field
     * @param target 目标对象
     * @param fieldName 字段名
     */
    @SuppressWarnings("unchecked")
    public static void setValue(Object target, String fieldName, Object value) {
        if (target == null) {
            return ;
        }
        String key = target.getClass() + "_" + fieldName;
        Field field = FIELD_MAP.get(key);
        if (field == null) {
            field = ReflectionUtils.findField(target.getClass(), fieldName);
            if (field == null) {
                return ;
            }
            field.setAccessible(true);
            FIELD_MAP.put(key, field);
        }
        try {
            field.set(target,value);
        } catch (Exception e) {
            log.error(e.getMessage());
        }

    }
}
